-
Notifications
You must be signed in to change notification settings - Fork 13.9k
Uplifts and extends clippy::needless-maybe-sized
into rustc
#145924
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Uplifts and extends clippy::needless-maybe-sized
into rustc
#145924
Conversation
This PR modifies Some changes occurred in src/tools/clippy cc @rust-lang/clippy |
clippy::needless-maybe-sized
into rustc
This comment has been minimized.
This comment has been minimized.
6f856bf
to
fe3f26f
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reviewed only the Clippy part.
fe3f26f
to
7306887
Compare
Thank you @samueltardieu for your feedback. Regarding the small changes to unrelated files, I believe that was my auto-formatter doing me a disservice, and I believe I have removed said changes from the commits. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I also believe I have addressed the other points you made, but do let me know if anything is amiss.
I think you still have to remove src/tools/clippy/tests/ui/needless_maybe_sized.rs
and src/tools/clippy/tests/ui/needless_maybe_sized.stderr
.
As far as I can tell those files have already been removed, let me know if I'm missing anything. Thank you |
These have been removed as far as I can tell, they're just showing as moves in Git as similar tests have been added to the rustc ui test suite w/ some changes. |
Oh yes, the subpar GitHub diff UI shows this only at the point of destination, not at the point of origin. |
7306887
to
8446c5a
Compare
I have added a .fixed file to the tests |
@rfcbot fcp merge |
For the other, |
Those are good points. And it seems we may have been unclear on what's included in this PR. Let's cancel the FCP for the moment. We'll then think about what we want here and repropose FCP. @rfcbot fcp cancel |
@traviscross proposal cancelled. |
Do we need to keep starting and stopping FCPs for small naming changes? This would be the third FCP that will be accepted for this patch, with one cancelled after two days because of the addition of a plural and another after waiting the full ten days. We're happy to name this whatever t-lang would like or split it into two lints, all we need is some clarity on what t-lang wants, ideally without having to wait another ten days for something that hasn't been at all controversial. |
Sorry about the noise. This is following our normal process for what to do in this circumstance, but applied to a small thing like this, it can of course look pretty silly. The purpose of the process is to make sure it's clear what we've agreed to exactly. I'll try to make sure we get it right in the next round. |
In the meeting, we had ended up a bit confused about what this lint as proposed in this PR does exactly. The documentation for it says this: declare_lint! {
/// The `redundant_sizedness_bounds` lint detects redundant sizedness bounds
/// applied to type parameters that are already otherwise implied.
///
/// ### Example
///
/// ```rust
/// // `T` must be `Sized` due to the bound `Clone`, thus `?Sized` is redundant.
/// fn f<T: Clone + ?Sized>(t: &T) {}
/// // `T` is `Sized` due to `Default` bound, thus the explicit `Sized` bound is redundant.
/// fn g<T: Default + Sized>(t: &T) {}
/// ```
///
/// {{produces}}
///
/// ### Explanation
///
/// Sizedness bounds that have no effect, as another bound implies `Sized`,
/// are redundant and can be misleading. This lint notifies the user of
/// these redundant bounds.
pub REDUNDANT_SIZEDNESS_BOUNDS,
Warn,
"a sizedness bound that is redundant due to another bound"
} That it, the documentation says it lints both against a relaxation of the default However, in the meeting, in looking at the tests, we couldn't find any examples of linting against a In the code, fn check_redundant_sizedness_bounds(
redundant_bound: DefId,
redundant_bound_polarity: BoundPolarity,
// ... I notice that it takes a polarity, so it seems designed to go both ways. However, in testing the branch, I can confirm that it does not lint against, e.g., What exactly was intended to be proposed here? Was the idea perhaps that this lint was to be expanded in the future? If we were to lint redundant In the meeting, we talked about how perhaps it would make sense to have a more general lint that fires when any trait appears in a list of bounds that is already implied due to being a supertrait. In that world, it might be a bit awkward to have a separate lint that did that just for
Thoughts on that? Would something like that achieve the goal here? |
The polarity is only mentioned because that's how The
Just uplifting of the existing redundant
Just the former.
I'm open to making it more general, subject to perf being okay (it could be an awful lot of querying supertraits of supertraits of traits, etc). |
Thanks for that. OK, so the case we want to address right now, then, is:
Probably I'd think to call this lint Why "unused"?In saying trait Tr {
type Ty where Self: Sized;
}
type Alias = dyn Tr<Ty = ()>;
//~^ warning: unnecessary associated type bound for dyn-incompatible
//~| associated type
//~| note: this associated type has a `where Self: Sized` bound, and
//~| while the associated type can be specified, it cannot be
//~| used because trait objects are never `Sized` I.e., there's a It also reminds me of fn f() -> u8 {
let mut x;
x = 1;
//~^ warning: value assigned to `x` is never read
//~| note: maybe it is overwritten before being read?
x = 2;
x
} I.e., we "overrode" the value of Similarly, fn foo(x: u8) {
if x < 0 {
//~^ warning: comparison is useless due to type limits
engage_safety();
}
} I.e., the comparison has semantic meaning but is "useless". In the same way, saying More broadly, this fits the model for me of what we lint as "unused", which are "things that have obvious semantic meaning but are useless, and which should all be deleted after turning off Why "relaxed bounds"?The core idea of this lint is that the user is trying to relax a bound but that, due to another bound, this relaxation isn't applied (or "used") by the compiler. While it happens to be true that the only bound that can be relaxed today in Rust is the default Why not "maybe"?I don't think we should use the word "maybe" to refer to "?X" bound relaxations. Either there's a predicate stating that a type parameter implements some trait or there isn't. While of course I get it -- the type argument provided may or may not implement the trait -- I just think "maybe" is speaking to the wrong thing here. It focuses on the type argument when what makes more sense to focus on is the type parameter. And the type parameter is not in a "maybe" state. Does |
I don't want to bikeshed: I like this lint, and I'd rather have the better version of this lint by any name sooner, than a perfectly-named lint later. But I'm currently working on making I think it's at least a nice-to-have if related SemVer ("across time") and rustc/clippy ("single point in time") lints have similar names. While "unused" seems plausibly reasonable in the "point in time" setting, it would imply a "becomes unused" lint in the SemVer setting. That doesn't seem like the right word at all to me. An example of the SemVer lint would catch e.g. a I think I'd rather say that
I'm in the middle of prototyping something that does that kind of awful lot of querying, for rustdoc JSON and cargo-semver-checks: #143197 Maybe we should talk? :) |
If you could elaborate this example, that'd be helpful. In particular, why are we worried about the bound here rather than the change to the trait? |
☔ The latest upstream changes (presumably #147779) made this pull request unmergeable. Please resolve the merge conflicts. |
I think the concern is that you may unknowingly start disallowing unsized types in your API when you intended to prevent that with |
This is exactly it. I'm concerned the user will see a rustc/clippy lints for I like all names @camsteffen and I proposed better than "unused" because they signal a higher severity. I'd still avoid "noop" if possible because I think it has the same risk, just not as much of it. |
It's still not coming into focus for me. Is this the concern? If the user has, in v1.1, pub fn f<T: ?Sized>(_: &dyn Deref<Target = T>) {} and then the user tries to ship, in v1.2, pub trait Tr: Sized {}
impl<T> Tr for T {}
pub fn f<T: ?Sized + Tr>(_: &dyn Deref<Target = T>) {} getting this warning, warning: `?Sized` has no effect due to another bound
--> src/main.rs
|
| pub fn f<T: ?Sized + Tr>(_: &dyn Deref<Target = T>) {}
| ^^^^^^ help: remove this unused bound relaxation
|
= note: generic parameters are assumed to be `Sized` unless
the bound is relaxed by writing `?Sized`, but here
this has no effect as another bound implies `Sized`
= note: `#[warn(unused_relaxed_bounds)]` on by default
note: the bound that implies `Sized` is here
--> src/main.rs
|
| pub fn f<T: ?Sized + Tr>(_: &dyn Deref<Target = T>) {}
| ^^ note: this bound implies `Sized`
|
note: `Sized` is implied by `Tr` as `Sized` is a supertrait
--> src/main.rs
|
| pub trait Tr: Sized {}
| ^^^^^ note: `Sized` is a supertrait
| then the idea is that the user is going to reason, "I added a new And then Is that right, or no? |
As context, I take for granted that E.g., if we have, pub trait Tr: Sized { fn f(self) -> u8 { 1 } }
impl<T> Tr for T {} and then somewhere else we write, pub struct W<T>(T);
impl<T> W<T> {
fn f(&self) -> u8 { 2 }
}
fn main() {
println!("{}", W(()).f());
} thinking that, "obviously this should print warning: method `f` is never used
--> src/main.rs:7:8
|
6 | impl<T> W<T> {
| ------------ method in this implementation
7 | fn f(&self) -> u8 { 2 }
| ^
|
= note: `#[warn(dead_code)]` (part of `#[warn(unused)]`) on by default that's a very serious warning. That causes me to sit up in my chair. I think, "if that code isn't being used, then what am I running? Not what I was expecting to run, obviously." Any One ignores unexpected |
I don't really object to unused personally. I agree whatever lint should make you stop and consider. However, there is an Responding to your first example, FWIW, a more subtle story would be that you have |
You've then made a breaking change to the trait. At that point, it seems like the use-site bound is the least of one's SemVer problems. |
Strongly agreed with this. "Users should take lints seriously" I agree with, but that is not the same statement as "users take lints seriously" which I don't think follows or is accurate in general. A great strength of Rust is setting up users to fall into the pit of success. Using We should also remember there are other analogous flavors of "you wrote code X which looks like it does Y but it actually does not." What we name this lint will set a precedent for name those future lints. One example with
Here,
This does not currently trigger any lint, neither in rustc nor in clippy. I believe it should. By precedent from here, would such a lint then be named If so, IMHO we'll end up dramatically redefining what the Not a pit of success choice.
I agree, the breaking change is to the trait. But that can be viciously difficult to catch, and I believe we should offer as many opportunities as possible for users to spot and stop this problem. Consider this example: playground
Let's assume there are suitable Furthermore, assume Say one then changes
It's thus all too easy for a trait bound change on a private trait, in an entirely non-pub corner of one's crate, to escalate and ripple across the ecosystem. SemVer breakage is a numbers game. IMHO we should play the numbers by doing our best to block that escalation path in as many places as possible. I believe this includes choosing a name for this lint that makes it harder to (mistakenly) ignore and just suppress away. To see how users might not understand the broader context of a lint and suppress it without due care, we only have to look as far as that |
I'm happy to answer questions and generally engage with the discussion, but I don't want to do so at the cost of shipping this lint at all. As I mentioned, my goal is not to bikeshed this :) I feel I've made my case, and I hope you've found it persuasive. If not, that's fine! Consider my voice heard and, as far as I'm concerned, feel free to proceed! |
Thanks for those details and discussion. To answer your question, given this: pub trait Tr: 'static {}
pub fn f<'a, T: Tr + 'a>() {}
// ^^ help: remove this bound Yes, I would suggest that bound is In some cases where we use "unused" today, one could even argue for stronger terms like "contradictory" or "conflicting". E.g.: #[unsafe(export_name = "foo")] //~ WARNING: unused attribute
#[unsafe(export_name = "bar")]
//~^ This could be made more subtle with `cfg_attr`, macros, etc.
pub fn f() {} Here, Broadly, I think the purpose of our lint names is to be as precise, concise, and consistent as possible, not to try to hit a particular scariness level. If we want to scare users, the lint output seems a better place to do that than the name. All that said, if in discussion we turn out to not be happy with "unused", here are some other names that have occurred to me:
The "ask for what you need" hierarchy of That's something to think about. Maybe, given our direction here, this new lint could itself be "subsumed". |
Ordinarily I would agree with this. But naming a lint We don't want
I'm happy with "subsumed" and it genuinely feels like the best name of the lot. It could also be the name of a new lint group for all these lints, which I think would be a good outcome. |
That's a good example. I feel convinced now that |
Given that it sounds like it was news to both of us that an ineffective |
We discussed this in the lang-team meeting and concluded two things
In particular, other kinds of redundancy are not clearly worth linting over, for example, given The "double role" of We were tempted to say: let's just do it for |
Can somebody update as to this question so we can review again in next triage? Thanks. |
Definitely a fan of marking bounds that "don't work", in some form. That said, I'd trying to think of whether there's potentially any reasons that you might want to write something for explicitness even if it's arguably useless. Like So I'm a fan of linting on the "conflict"-y cases like As a result I'd be fine focusing to the |
For my part, I'm not sure I would be happy with linting on At the same time, I'm sympathetic to @scottmcm's point that writing out the subtrait explicitly in the bound can sometimes be desired to improve local reasoning or (as I'd maybe see it) as a static assertion. |
Fixes #140962
Uplift the
clippy::needless_maybe_sized
lint into rustc asredundant_sizedness_bounds
. This is useful for #144404 as it deals with redundant bound that would need to be addressed during migration.redundant_sizedness_bounds
(warn-by-default)
The
needless_maybe_sized
lint checks that?Sized
bounds aren't redundant. This lint is extended to do the equivalent checks for the sizedness traits introduced by #144404 and thus renamed fromneedless_maybe_sized
.Example
Explanation
Relaxing a default
Sized
bound with?Sized
does nothing when there's another bound with aSized
supertrait (Clone
in the example above)@rustbot label: +I-lang-nominated
r? @lcnr
cc @flip1995
For Clippy:
changelog: Moves: Uplifted
clippy::needless_maybe_sized
into rustc asredundant_sizedness_bounds